const babel = require("@babel/core");
const vueTemplateCompiler = require("vue-template-compiler");

class CountdownPlugin {
  apply(compiler) {
    // 监听webpack打包事件
    compiler.hooks.emit.tapAsync("CountdownPlugin", (compilation, callback) => {
      const assets = compilation.assets;

      // 遍历所有输入的文件
      for (const filename in assets) {
        if (/\.js$/.test(filename)) {
          const inputCode = assets[filename].source();

          // 使用babel转换ES6代码
          const outputCode = babel.transformSync(inputCode, {
            plugins: ["@vue/babel-plugin-transform-vue-jsx"],
          });

          // 使用vue-template-compiler将模板代码解析成AST语法树
          const template = outputCode.code.match(
            /<template>([\s\S]+)<\/template>/
          )[1];
          const ast = vueTemplateCompiler.compile(template).ast;

          // 使用AST语法树生成Vue组件的render函数
          function generateRender(ast) {
            // 处理节点指令
            function processDirective(el) {
              el.directives.forEach((dir) => {
                if (dir.name === "show") {
                  el.if = dir.value ? `(${dir.value}) && (${el.if})` : el.if;
                } else if (dir.name === "if") {
                  el.if = dir.exp || "";
                }
              });
            }

            // 处理子节点
            function genChildren(ast) {
              if (ast.children.length) {
                return ast.children
                  .map((child) => {
                    if (child.type === 1) {
                      return generateElement(child);
                    } else if (child.type === 2) {
                      return `\`${child.expression}\``;
                    } else {
                      return child.text;
                    }
                  })
                  .join(",");
              } else {
                return "";
              }
            }

            // 处理元素节点
            function generateElement(el) {
              processDirective(el);

              const children = genChildren(el);
              const attrs = el.attrs
                .map((attr) => {
                  return `${attr.name}: '${attr.value}'`;
                })
                .join(",");

              if (el.if) {
                return `(${el.if}) ? h('${el.tag}', {${attrs}}, [${children}]) : null`;
              } else {
                return `h('${el.tag}', {${attrs}}, [${children}])`;
              }
            }

            return `function () {
              var _this = this;

              return ${generateElement(ast)};
            }`;
          }

          const render = generateRender(ast);

          // 替换模块代码为Vue组件代码
          const output = `
            const Vue = require('vue');

            const component = Vue.extend({
              render: new Function(${JSON.stringify(render)}).bind(this)
            });
            module.exports = component;
          `;
          assets[filename] = {
            source: () => output,
            size: () => output.length,
          };
        }
      }

      callback();
    });
  }
}

module.exports = CountdownPlugin;
